package com.adopapa.ethexplorer.contoller;

import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.web3j.protocol.core.methods.response.admin.AdminNodeInfo.NodeInfo;
import org.web3j.protocol.core.methods.response.admin.AdminPeers.Peer;

import com.adopapa.ethexplorer.domain.BlockInfo;
import com.adopapa.ethexplorer.domain.ContractInfoService;
import com.adopapa.ethexplorer.domain.TransactionInfo;
import com.adopapa.ethexplorer.service.BlockInfoService;
import com.adopapa.ethexplorer.service.ChaindataService;
import com.adopapa.ethexplorer.service.TransactionInfoService;
import com.adopapa.ethexplorer.utils.Tools;

import reactor.core.publisher.Mono;

@RestController
@RequestMapping("/api/v1/index")
public class IndexController extends BaseController {

	@Autowired
	private ChaindataService chaindataService;
	@Autowired
	private BlockInfoService blockInfoService;
	@Autowired
	private ContractInfoService contractInfoService;
	@Autowired
	private TransactionInfoService transactionInfoService;

	@GetMapping(path = "/search", produces = MediaType.APPLICATION_JSON_VALUE)
	public Mono<Map<String, Object>> search(@RequestParam String searchText) {
		try {
			if (Tools.empty(searchText)) {
				return Mono.just(ok());
			}

			Map<String, Object> result = new HashMap<String, Object>();
			if (!searchText.startsWith("0x")) {
				try {
					Long number = Long.valueOf(searchText.trim());
					BlockInfo blockInfo = blockInfoService.getBlockInfoByNumber(number);
					if (!Tools.empty(blockInfo)) {
						result.put("searchType", "blockNumber");
						result.put("searchText", blockInfo.getHash());
						result.put("result", blockInfo);
						return Mono.just(ok(result));
					}

					return Mono.just(ok());
				} catch (Exception e) {
					logger.error(ExceptionUtils.getStackTrace(e));

					return Mono.just(ok());
				}
			}
			TransactionInfo transactionInfo = transactionInfoService.getTransactionInfoByHash(searchText.trim());
			if (!Tools.empty(transactionInfo)) {
				result.put("searchType", "txHash");
				result.put("searchText", transactionInfo.getHash());
				result.put("result", transactionInfo);

				return Mono.just(ok(result));
			}

			BlockInfo blockInfo = blockInfoService.getBlockInfoByHash(searchText.trim());
			if (!Tools.empty(blockInfo)) {
				result.put("searchType", "blockHash");
				result.put("searchText", blockInfo.getHash());
				result.put("result", blockInfo);
				return Mono.just(ok(result));
			}

			BigInteger balance = chaindataService.getBalance(searchText.trim());
			if (!Tools.empty(balance)) {
				result.put("searchType", "balance");
				result.put("searchText", searchText.trim());
				result.put("result", balance);
				return Mono.just(ok(result));
			}

			return Mono.just(ok());

		} catch (Exception e) {
			logger.error(ExceptionUtils.getStackTrace(e));
			return Mono.just(ok());
		}

	}

	@GetMapping(path = "/statdata", produces = MediaType.APPLICATION_JSON_VALUE)
	public Mono<Map<String, Object>> getStatData() {
		BlockInfo lastBlockInfo = blockInfoService.getLastBlockInfo();
		Long blockNumber = 0L;
		if (!Tools.empty(lastBlockInfo)) {
			blockNumber = blockInfoService.getLastBlockInfo().getNumber();
		}
		Long txNumber = transactionInfoService.getTransactionNumber();
		Long contractNumber = contractInfoService.getContractNumber();

		Long nodeNumber = 0L;
		NodeInfo nodeInfo = chaindataService.getAdminNodeInfo();
		List<Peer> peers = chaindataService.getAdminPeers();
		if (!Tools.empty(nodeInfo)) {
			nodeNumber += 1L;
		}
		if (!Tools.empty(peers)) {
			nodeNumber += peers.size();
		}

		Map<String, Object> result = new HashMap<String, Object>();
		result.put("blockNumber", blockNumber);
		result.put("txNumber", txNumber);
		result.put("contractNumber", contractNumber);
		result.put("nodeNumber", nodeNumber);

		return Mono.just(ok(result));

	}

}
